from apscheduler.schedulers.asyncio import AsyncIOScheduler
from apscheduler.schedulers.background import BackgroundScheduler
import time
import asyncio
from datetime import datetime, timedelta
from loguru import logger
from sqlalchemy.orm import Session
from typing import Dict, Any

from Util.AuthUtil import get_token
from config.Database import get_db
from services.SignApplyInfoServe import SignApplyInfoServe
from schemas.SignApplyInfo import SignApplyInfoUpdate
from Util.MailUtil import send_mail
from Util.ActivityUtil import sign_in_out

# 存储所有定时任务
sign_tasks = {}


async def execute_sign_task(sign_apply_id: int):
    """执行签到任务的具体实现
    
    Args:
        sign_apply_id: 签到申请记录ID
    """
    logger.info(f"执行签到任务，记录ID: {sign_apply_id}")

    # 获取数据库会话
    db = next(get_db())
    try:
        # 获取签到申请信息
        sign_apply_info = SignApplyInfoServe.get_by_id(db, sign_apply_id)
        SignApplyInfoServe.mark_as_applied(db, sign_apply_id)
        if not sign_apply_info:
            logger.error(f"无法找到{sign_apply_info.nickName}{sign_apply_info.username}ID为 {sign_apply_id}{sign_apply_info.activityName} 的签到申请信息")
            return
        if sign_apply_info.password is None:
            logger.error(f"无法找到{sign_apply_info.nickName}{sign_apply_info.username}ID为 {sign_apply_id} {sign_apply_info.activityName}的全部签到申请信息")
            return

        token = await get_token(sign_apply_info.username, sign_apply_info.password)
        if not token:
            logger.error(f"无法获取到{sign_apply_info.nickName}{sign_apply_info.username}的token 用来自动签到")
            return
        
        # 执行实际的签到/签退操作
        result = await sign_in_out(
            id=sign_apply_info.activityId, 
            token=token,  # 这里需要获取token，可能需要从用户名密码获取
            sign_type=sign_apply_info.signType  # 1是签到，2是签退
        )
        
        success = False
        message = ""
        
        if result and "msg" in result:
            message = result["msg"]
            if "成功" in message:
                success = True
                logger.info(f"签到任务执行成功 - 活动ID: {sign_apply_info.activityId}, 用户: {sign_apply_info.username}")
                # 标记任务为已成功执行
                SignApplyInfoServe.update(db, sign_apply_id, SignApplyInfoUpdate(isSuccess=1, remark=message))
            else:
                logger.error(f"签到任务执行失败 - 活动ID: {sign_apply_info.activityId}, 用户: {sign_apply_info.username}, 原因: {message}")
                # 标记任务为执行失败
                SignApplyInfoServe.update(db, sign_apply_id, SignApplyInfoUpdate(isSuccess=0, remark=message))
        else:
            message = "签到/签退请求失败，未收到有效响应"
            logger.error(f"签到任务执行失败 - 活动ID: {sign_apply_info.activityId}, 用户: {sign_apply_info.username}, 原因: {message}")
            # 标记任务为执行失败
            SignApplyInfoServe.update(db, sign_apply_id, SignApplyInfoUpdate(isSuccess=0, remark=message))
        
        # 发送邮件通知
        # 查询用户邮箱
        from sqlalchemy import text
        email_query = text("SELECT email FROM submitinfo WHERE username = :username LIMIT 1")
        email_result = db.execute(email_query, {"username": sign_apply_info.username}).fetchone()
        
        if email_result and email_result[0]:
            email = email_result[0]
            subject = f"{'签到' if sign_apply_info.signType == 1 else '签退'}{'成功' if success else '失败'}通知"
            content = f"""
            <h2>{'签到' if sign_apply_info.signType == 1 else '签退'}{'成功' if success else '失败'}通知</h2>
            <p>活动名称：{sign_apply_info.activityName}</p>
            <p>用户名：{sign_apply_info.username}</p>
            <p>状态：{'成功' if success else '失败'}</p>
            <p>时间：{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}</p>
            <p>消息：{message}</p>
            """
            send_mail(email, subject, content)
            logger.info(f"已发送邮件通知到 {email}")
        else:
            logger.warning(f"未找到用户 {sign_apply_info.username} 的邮箱信息，无法发送通知")
        
        # 从任务字典中移除已完成的任务
        task_key = f"{sign_apply_info.username}_{sign_apply_info.activityId}"
        if task_key in sign_tasks:
            del sign_tasks[task_key]
            
    except Exception as e:
        logger.error(f"执行签到任务失败，记录ID: {sign_apply_id}, 错误: {str(e)}")
        # 标记任务为失败
        SignApplyInfoServe.update(db, sign_apply_id, SignApplyInfoUpdate(isSuccess=0, remark=f"执行失败: {str(e)}"))
        
        # 尝试发送失败通知邮件
        try:
            from sqlalchemy import text
            email_query = text("SELECT email FROM submitinfo WHERE username = :username LIMIT 1")
            email_result = db.execute(email_query, {"username": sign_apply_info.username}).fetchone()
            
            if email_result and email_result[0]:
                email = email_result[0]
                subject = f"{'签到' if sign_apply_info.signType == 1 else '签退'}失败通知"
                content = f"""
                <h2>{'签到' if sign_apply_info.signType == 1 else '签退'}失败通知</h2>
                <p>活动名称：{sign_apply_info.activityName}</p>
                <p>用户名：{sign_apply_info.username}</p>
                <p>状态：失败</p>
                <p>时间：{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}</p>
                <p>错误：{str(e)}</p>
                """
                send_mail(email, subject, content)
        except Exception as mail_error:
            logger.error(f"发送失败通知邮件时出错: {str(mail_error)}")
    finally:
        db.close()

async def schedule_sign_task(sign_apply_id: int, wait_seconds: float):
    """定时任务调度函数
    
    Args:
        sign_apply_id: 签到申请记录ID
        wait_seconds: 等待时间（秒）
    """
    try:
        # 等待到签到时间
        await asyncio.sleep(wait_seconds)
        # 执行签到
        await execute_sign_task(sign_apply_id)
    except asyncio.CancelledError:
        logger.info(f"签到任务 {sign_apply_id} 已取消")
    except Exception as e:
        logger.error(f"签到任务 {sign_apply_id} 出错: {str(e)}")

async def add_sign_task(sign_apply_id: int, sign_time: datetime):
    """添加签到定时任务
    
    Args:
        sign_apply_id: 签到申请记录ID
        sign_time: 签到时间（datetime对象，已经计算好的确切时间点）
    """
    try:
        # 获取数据库会话
        db = next(get_db())
        try:
            # 获取签到申请信息
            sign_apply_info = SignApplyInfoServe.get_by_id(db, sign_apply_id)
            if not sign_apply_info:
                logger.error(f"无法找到ID为 {sign_apply_id} 的签到申请信息")
                return
                
            # 生成任务唯一标识
            task_key = f"{sign_apply_info.username}_{sign_apply_info.activityId}"
            
            # 直接使用传入的sign_time作为执行时间
            execute_time = sign_time
            
            # 计算等待时间（秒）
            now = datetime.now()
            wait_seconds = (execute_time - now).total_seconds()
            
            # 生成任务ID
            task_id = f"sign_task_{task_key}_{int(time.time())}"
            
            logger.info(f"成功添加签到定时任务，记录ID: {sign_apply_id}, 执行时间: {execute_time}, 等待: {wait_seconds}秒")
            
            # 存储任务信息
            sign_tasks[task_key] = {
                'job_id': task_id,
                'execute_time': execute_time,
                'sign_apply_id': sign_apply_id
            }

            if sign_apply_info.isPush == 0:
                # 查询用户邮箱
                from sqlalchemy import text
                email_query = text("SELECT email FROM submitinfo WHERE username = :username LIMIT 1")
                email_result = db.execute(email_query, {"username": sign_apply_info.username}).fetchone()
                
                if email_result and email_result[0]:
                    email = email_result[0]
                    subject = f"{'签到' if sign_apply_info.signType == 1 else '签退'}任务已创建"
                    content = f"""
                    <h2>{'签到' if sign_apply_info.signType == 1 else '签退'}任务已创建</h2>
                    <p>活动名称：{sign_apply_info.activityName}</p>
                    <p>用户名：{sign_apply_info.username}</p>
                    <p>计划执行时间：{execute_time.strftime('%Y-%m-%d %H:%M:%S')}</p>
                    <p>任务ID：{sign_apply_id}</p>
                    """
                    # 标记为已发送邮箱
                    SignApplyInfoServe.mark_as_pushed(db, sign_apply_id)
                    send_mail(email, subject, content)
                    logger.info(f"已发送任务创建通知邮件到 {email}")
            
            if wait_seconds <= 0:
                # 如果已经到了签到时间，立即执行
                logger.info(f"签到任务 {sign_apply_id} 已经到时间，立即执行")
                asyncio.create_task(execute_sign_task(sign_apply_id))
            else:
                # 创建定时任务
                sign_tasks[task_key]['task'] = asyncio.create_task(
                    schedule_sign_task(sign_apply_id, wait_seconds)
                )
        finally:
            db.close()
    except Exception as e:
        logger.error(f"添加签到定时任务失败，记录ID: {sign_apply_id}, 错误: {str(e)}")

async def scan_and_schedule_sign_tasks():
    """扫描数据库并调度签到任务
    
    此函数会：
    1. 查询数据库中未申请且签到时间大于0的记录
    2. 为每条记录创建定时任务
    3. 定时任务将在 当前时间 + signTime分钟 + 60分钟 后执行
    """
    logger.info(f"扫描签到任务执行：当前时间 {time.strftime('%Y-%m-%d %H:%M:%S')}")
    # 获取数据库会话
    db = next(get_db())
    try:
        # 查询所有未申请且签到时间大于0的记录
        pending_applications = SignApplyInfoServe.get_pending_applications(db)
        
        for sign_apply_info in pending_applications:
            # 生成任务唯一标识
            task_key = f"{sign_apply_info.username}_{sign_apply_info.activityId}"
            
            # 检查是否已经添加过任务
            if task_key in sign_tasks:
                logger.debug(f"签到任务已存在，跳过：记录ID {sign_apply_info.id}")
                continue
            
            # 添加定时任务
            await add_sign_task(sign_apply_info.id, sign_apply_info.signTime)
            
        logger.info(f"扫描完成，处理了 {len(pending_applications)} 条待处理记录")
        
    except Exception as e:
        logger.error(f"扫描签到任务失败: {str(e)}")
    finally:
        db.close()
